import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mwm/widget/load.dart';

import '../exception/exception.dart';
import '../util/util.dart';
import 'impl.dart';

abstract class _BaseHttp {
  int what;
  Object tag;

  _BaseHttp setWhat(int what);

  _BaseHttp setTag(Object tag);

  String _httpOrHttps();

  String _defaultPort();

  String _baseUrl(String serverIp);

  String _serverNameSpace();

  String _url(String ipAdd, String method);

  void callback(HttpCallbackImpl subscriberImpl);

  void execute(
      {String url,
      String method,
      Map<String, dynamic> paramBody,
      Map<String, dynamic> multipartBody});

  void get({String url, String method, Map<String, dynamic> paramBody});
}

abstract class _Http extends _BaseHttp implements ServerImpl {
  @override
  _BaseHttp setTag(Object tag) {
    this.tag = tag;
    return this;
  }

  @override
  _BaseHttp setWhat(int what) {
    this.what = what;
    return this;
  }

  @override
  String _httpOrHttps() {
    return 'http';
  }

  @override
  String _defaultPort() {
    return ServerImpl.DEFAULT_PORT;
  }

  @override
  String _serverNameSpace() {
    return ServerImpl.NAMESPACE;
  }

  @override
  String _baseUrl(String serverIp) {
    return ServerImpl.getBaseUrl(
        _httpOrHttps(), serverIp, _defaultPort(), _serverNameSpace());
  }

  @override
  String _url(String ipAdd, String method) {
    return '${_baseUrl(ipAdd)}${ServerImpl.ACTION}$method';
  }
}

class HttpKit extends _Http {
  BuildContext _context;
  HttpCallbackImpl _subscriberImpl;
  OverlayEntry _entry;
  var _dio = Dio();

  static HttpKit _httpKit;

  static HttpKit getInstance({BuildContext context, int what, Object tag}) {
    if (null == _httpKit) {
      _httpKit = HttpKit(context: context);
      _httpKit.setWhat(what).setTag(tag);
    } else {
      _httpKit._setContext(context);
      _httpKit.setWhat(what).setTag(tag);
    }
    return _httpKit;
  }

  HttpKit({BuildContext context}) {
    _setContext(context);
    _dio.options.baseUrl = '';
    _dio.options.connectTimeout = 60000;
    _dio.options.receiveTimeout = 15000;
    //_dio.options.sendTimeout = 60000;
    //_dio.interceptors.add(CommonInterceptor());
    _dio.interceptors.add(LogInterceptor());
  }

  _setContext(BuildContext context) {
    if (null == _dio) _dio = Dio();
    this._context = context;
  }

  Future<String> getIcon({String method, String username}) async {
    String baseUrl = await PreferUtil.getDefault(
        ServerImpl.KEY_BASEURL, ServerImpl.DEFAULT_IPADD);
    return '${_url(baseUrl, method)}&username=$username';
  }

  @override
  void callback(HttpCallbackImpl subscriberImpl) {
    _subscriberImpl = subscriberImpl;
  }

  ///[url] 完整的访问地址
  ///[method] 调用方法 {action.do?method=method}
  ///二者选其一即可
  @override
  void execute(
      {String url,
      String method,
      Map<String, dynamic> paramBody,
      Map<String, dynamic> multipartBody}) {

    if (TextUtils.isEmpty(url) && TextUtils.isEmpty(method)) {
      _error(ExecuteException('url and method Cannot be null at the same time！'
          '{\'url\'=\'$url\',\'method\'=\'$method\'}'));
      return;
    }
    Log.out('请求参数: ' + paramBody.toString());
    try {
      _show();
      PreferUtil.getDefault(ServerImpl.KEY_BASEURL, ServerImpl.DEFAULT_IPADD)
          .then((baseUrl) {
        String httpUrl = _url(baseUrl, method);
        _dio
            .post(TextUtils.isEmpty(url) ? httpUrl : url,
                queryParameters: paramBody,
                data: FormData.fromMap(multipartBody))
            .then((response) {
              String result = response.data;
              Log.out('response', tag: result);
              //String result = UrlCode.decode(body);
              if (!TextUtils.isEmpty(result) && result.startsWith('Error ')) {
                _error(result);
              } else {
                _result(result);
              }
              _complete();
            })
            .timeout(Duration(seconds: 15))
            .whenComplete(() {
              _complete();
              _releaseDio();
            })
            .catchError((error) {
              _error(error);
            });
      });
    } catch (error) {
      _error(error);
    }
  }

  ///[url] 完整的访问地址
  ///[method] 调用方法 {action.do?method=method}
  ///二者选其一即可
  @override
  void get({String url, String method, Map<String, dynamic> paramBody}) {

    if (TextUtils.isEmpty(url) && TextUtils.isEmpty(method)) {
      _error(ExecuteException('url and method Cannot be null at the same time！'
          '{\'url\'=\'$url\',\'method\'=\'$method\'}'));
      return;
    }
    try {
      PreferUtil.getDefault(ServerImpl.KEY_BASEURL, ServerImpl.DEFAULT_IPADD)
          .then((baseUrl) {
        String httpUrl = _url('192.168.0.110:8080', method);
        _dio
            .get(TextUtils.isEmpty(url) ? httpUrl : url,
                queryParameters: paramBody)
            .then((response) {
              String result = response.data;
              //String result = UrlCode.decode(body);
              if (!TextUtils.isEmpty(result) && result.startsWith('Error ')) {
                _error(result);
              } else {
                _result(result);
              }
              _complete();
            })
            .timeout(Duration(seconds: 15))
            .whenComplete(() {
              _complete();
              _releaseDio();
            })
            .catchError((error) {
              _error(error);
            });
      });
    } catch (error) {
      _error(error);
    }
  }

  void _show() {
    if (null != _context) {
      _entry = OverlayEntry(
        builder: (_) => ProgressView(
          '加载中...',
          _outsideCancel,
          outSideCancel: false,
        ),
      );
      Overlay.of(_context).insert(_entry);
    }
  }

  void _dismiss() {
    if (null != _entry) {
      _entry?.remove();
      _entry = null;
    }
  }

  ///可不用释放Dio
  void _releaseDio() {
    /*if (null != _dio) {
      _dio.close();
      _dio = null;
    }*/
  }

  void _outsideCancel() {
    try {
      _complete();
      _releaseDio();
      _error(CancelException('已取消操作'));
    } catch (e) {
      //
    }
  }

  void _result(String decodeResult) {
    if (null != _subscriberImpl)
      _subscriberImpl.onResult(null == what ? 2 : what, tag, decodeResult);
  }

  void _error(Object error) {
    _dismiss();
    if (TextUtils.equals(
        "已取消操作", null == error ? '' : ConnectUtil.getError(error))) {
      if (null != _subscriberImpl)
        _subscriberImpl.onError(
            null == what ? 2 : what, tag, ConnectUtil.getError(error));
      _subscriberImpl = null;
    } else {
      if (null != _subscriberImpl)
        _subscriberImpl.onError(
            null == what ? 2 : what, tag, ConnectUtil.getError(error));
    }
  }

  void _complete() {
    _dismiss();
  }
}
